package cn.yuanqiao.archive.uploadfile.service.impl;

import cn.yuanqiao.archive.uploadfile.constant.FileConstant;
import cn.yuanqiao.archive.uploadfile.dto.FileUploadDTO;
import cn.yuanqiao.archive.uploadfile.dto.FileUploadRequestDTO;
import cn.yuanqiao.archive.uploadfile.service.SliceUploadStrategy;
import cn.yuanqiao.archive.uploadfile.util.FileUtils;
import cn.yuanqiao.archive.uploadfile.util.SpringContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;

public abstract class SliceUploadTemplate implements SliceUploadStrategy {
    private static final Logger log = LoggerFactory.getLogger(SliceUploadTemplate.class);

    public abstract boolean upload(FileUploadRequestDTO param);

    protected File createTmpFile(FileUploadRequestDTO param) {
        FileUtils fileUtils = SpringContextHolder.getBean(FileUtils.class);
        param.setPath(FileUtils.withoutHeadAndTailDiagonal(param.getPath()));
        String fileName = param.getFile().getOriginalFilename();  //获取文件名
        String uploadDirPath = fileUtils.getPath(param);  //前端传的path
        String tempFileName = fileName + "_tmp";
        File tmpDir = new File(uploadDirPath);
        if (!tmpDir.exists()) {
            tmpDir.mkdirs();
        }
        File tmpFile = new File(uploadDirPath, tempFileName);
        return tmpFile;
    }

    @Override
    public FileUploadDTO sliceUpload(FileUploadRequestDTO param) {

        boolean isOk = this.upload(param);
        if (isOk) {
            File tmpFile = this.createTmpFile(param);
            //return this.saveAndFileUploadDTO(param.getFile().getOriginalFilename(), tmpFile,param.getPath());
            return this.saveAndFileUploadDTO(param, tmpFile);
        }
        String md5 = FileUtils.getFileMD5(param.getFile());

        Map<Integer, String> map = new HashMap<>();
        map.put(param.getChunk(), md5);
        FileUploadDTO fileUploadDTO = new FileUploadDTO();
        fileUploadDTO.setChunkMd5Info(map);
        return fileUploadDTO;
    }

    /**
     * 检查并修改文件上传进度
     */
    public boolean checkAndSetUploadProgress(FileUploadRequestDTO param, String uploadDirPath) {
        String fileName = param.getFile().getOriginalFilename();
        File confFile = new File(uploadDirPath, fileName + ".conf");
        byte isComplete = 0;
        RandomAccessFile accessConfFile = null;
        try {
            accessConfFile = new RandomAccessFile(confFile, "rw");
            //把该分段标记为 true 表示完成
            log.info("set part " + param.getChunk() + " complete");
            //创建conf文件文件长度为总分片数，每上传一个分块即向conf文件中写入一个127，那么没上传的位置就是默认0,已上传的就是Byte.MAX_VALUE 127
            accessConfFile.setLength(param.getChunks());
            accessConfFile.seek(param.getChunk() - 1);
            //accessConfFile.seek(param.getChunk());
            accessConfFile.write(Byte.MAX_VALUE);

            //completeList 检查是否全部完成,如果数组里是否全部都是127(全部分片都成功上传)
            byte[] completeList = FileUtils.readFileToByteArray(confFile);
            isComplete = Byte.MAX_VALUE;
            for (int i = 0; i < completeList.length && isComplete == Byte.MAX_VALUE; i++) {
                //与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUE
                isComplete = (byte) (isComplete & completeList[i]);
                log.info("check part " + (i+1) + " complete?:" + completeList[i]);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            FileUtils.close(accessConfFile.getChannel());
        }
        return setUploadProgress2Redis(param, uploadDirPath, fileName, confFile, isComplete);
    }

    /**
     * 把上传进度信息存进redis
     */
    private boolean setUploadProgress2Redis(FileUploadRequestDTO param, String uploadDirPath,
                                            String fileName, File confFile, byte isComplete) {
        String[] split = param.getMd5().split("_");
        RedisTemplate redisUtil = SpringContextHolder.getBean("redisTemplate");
        if (isComplete == Byte.MAX_VALUE) {
            redisUtil.opsForHash().delete(FileConstant.FILE_UPLOAD_STATUS,split[1]);
            //redisUtil.opsForHash().put(FileConstant.FILE_UPLOAD_STATUS, param.getMd5(), "true");
            redisUtil.delete(FileConstant.FILE_MD5_KEY + split[1]);
            confFile.delete();
            return true;
        } else {
            if(!redisUtil.opsForHash().hasKey(FileConstant.FILE_UPLOAD_STATUS, split[1])){
                redisUtil.opsForHash().put(FileConstant.FILE_UPLOAD_STATUS, split[1], "false");
                redisUtil.opsForValue().set(FileConstant.FILE_MD5_KEY + split[1],
                        uploadDirPath + FileConstant.FILE_SEPARATORCHAR + fileName + ".conf");
            }
            return false;
        }
    }

    /**
     * 保存文件操作
     */
    public FileUploadDTO saveAndFileUploadDTO(FileUploadRequestDTO param, File tmpFile) {
        FileUploadDTO fileUploadDTO = null;
        try {
            String fileName = param.getFile().getOriginalFilename();
            String path = param.getPath();

            fileUploadDTO = renameFile(tmpFile, fileName,path);
            if (fileUploadDTO.isUploadComplete()) {
                System.out.println("upload complete !!" + fileUploadDTO.isUploadComplete() + " name=" + fileName);

                //String zipPath = fileUploadDTO.getPath();
                //String unZipCharSet = param.getUnZipCharSet();
                //if(!StringUtils.isNotEmpty(unZipCharSet)) unZipCharSet = "GBK";
                //String unZipPath = path+FileConstant.FILE_SEPARATORCHAR+param.getMd5();
                //ZipUtils.unZip(new File(zipPath),unZipPath,unZipCharSet);


            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return fileUploadDTO;
    }

    /**
     * 文件重命名
     *
     * @param toBeRenamed   将要修改名字的文件
     * @param toFileNewName 新的名字
     */
    private FileUploadDTO renameFile(File toBeRenamed, String toFileNewName,String path) {
        //检查要重命名的文件是否存在，是否是文件
        FileUploadDTO fileUploadDTO = new FileUploadDTO();
        if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
            log.info("File does not exist: {}", toBeRenamed.getName());
            fileUploadDTO.setUploadComplete(false);
            return fileUploadDTO;
        }
        String ext = FileUtils.getExtension(toFileNewName);
        //String p = toBeRenamed.getParent();
        //String filePath = "E:" + FileConstant.FILE_SEPARATORCHAR +"testGuajie" + FileConstant.FILE_SEPARATORCHAR + toFileNewName;
        String filePath = path+FileConstant.FILE_SEPARATORCHAR+toFileNewName;
        File newFile = new File(filePath);
        //修改文件名
        boolean uploadFlag = toBeRenamed.renameTo(newFile);
        fileUploadDTO.setMtime(System.currentTimeMillis());
        fileUploadDTO.setUploadComplete(uploadFlag);
        fileUploadDTO.setPath(filePath);
        fileUploadDTO.setSize(newFile.length());
        fileUploadDTO.setFileExt(ext);
        fileUploadDTO.setFileId(toFileNewName);
        return fileUploadDTO;
    }

}
